package requests

import (
	"encoding/json"

	"github.com/liyaozong/mengo/contracts/common"
	dtoCommon "github.com/liyaozong/mengo/contracts/dtos/common"
	"github.com/liyaozong/mengo/contracts/errors"
	"github.com/liyaozong/mengo/contracts/models"
)

// AddDeviceRequest defines the Request Content for POST Device DTO.
type AddDeviceRequest struct {
	dtoCommon.BaseRequest `json:",inline"`
	Device                models.Device `json:"device"`
}

// Validate satisfies the Validator interface
func (d AddDeviceRequest) Validate() error {
	err := common.Validate(d)
	return err
}

// UnmarshalJSON implements the Unmarshaler interface for the AddDeviceRequest type
func (d *AddDeviceRequest) UnmarshalJSON(b []byte) error {
	var alias struct {
		dtoCommon.BaseRequest
		Device models.Device
	}
	if err := json.Unmarshal(b, &alias); err != nil {
		return errors.NewCommonMenGo(errors.KindContractInvalid, "Failed to unmarshal request body as JSON.", err)
	}

	*d = AddDeviceRequest(alias)

	// validate AddDeviceRequest DTO
	if err := d.Validate(); err != nil {
		return err
	}
	return nil
}

// AddDeviceReqToDeviceModels transforms the AddDeviceRequest DTO array to the Device model array
func AddDeviceReqToDeviceModels(addRequests []AddDeviceRequest) (Devices []models.Device) {
	for _, req := range addRequests {
		Devices = append(Devices, req.Device)
	}
	return Devices
}

type UpdateDevice struct {
	Id             *string     `json:"id" validate:"required_without=Name,edgex-dto-uuid"`
	Name           *string     `json:"name" validate:"required_without=Id,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
	Description    *string     `json:"description" validate:"omitempty"`
	AdminState     *string     `json:"adminState" validate:"omitempty,oneof='LOCKED' 'UNLOCKED'"`
	OperatingState *string     `json:"operatingState" validate:"omitempty,oneof='UP' 'DOWN' 'UNKNOWN'"`
	ServiceName    *string     `json:"serviceName" validate:"omitempty,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
	ProfileName    *string     `json:"profileName" validate:"omitempty,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"`
	Labels         []string    `json:"labels"`
	Location       interface{} `json:"location"`
	// AutoEvents     []AutoEvent                   `json:"autoEvents" validate:"dive"`
	// Protocols      map[string]ProtocolProperties `json:"protocols" validate:"omitempty,gt=0"`
	Tags       map[string]any `json:"tags"`
	Properties map[string]any `json:"properties"`
}

// UpdateDeviceRequest defines the Request Content for PUT event as pushed DTO.
type UpdateDeviceRequest struct {
	dtoCommon.BaseRequest `json:",inline"`
	Device                UpdateDevice `json:"device"`
}

// Validate satisfies the Validator interface
func (d UpdateDeviceRequest) Validate() error {
	err := common.Validate(d)
	return err
}

// UnmarshalJSON implements the Unmarshaler interface for the UpdateDeviceRequest type
func (d *UpdateDeviceRequest) UnmarshalJSON(b []byte) error {
	var alias struct {
		dtoCommon.BaseRequest
		Device UpdateDevice
	}
	if err := json.Unmarshal(b, &alias); err != nil {
		return errors.NewCommonMenGo(errors.KindContractInvalid, "Failed to unmarshal request body as JSON.", err)
	}

	*d = UpdateDeviceRequest(alias)

	// validate UpdateDeviceRequest DTO
	if err := d.Validate(); err != nil {
		return err
	}
	return nil
}

// ReplaceDeviceModelFieldsWithDTO replace existing Device's fields with DTO patch
func ReplaceDeviceModelFieldsWithDTO(device *models.Device, patch UpdateDevice) {
	if patch.Description != nil {
		device.Description = *patch.Description
	}
	if patch.AdminState != nil {
		device.AdminState = models.AdminState(*patch.AdminState)
	}
	if patch.OperatingState != nil {
		device.OperatingState = models.OperatingState(*patch.OperatingState)
	}
	if patch.ServiceName != nil {
		device.ServiceName = *patch.ServiceName
	}
	if patch.ProfileName != nil {
		device.ProfileName = *patch.ProfileName
	}
	if patch.Labels != nil {
		device.Labels = patch.Labels
	}
	if patch.Location != nil {
		device.Location = patch.Location
	}
	// if patch.AutoEvents != nil {
	// 	device.AutoEvents = dtos.ToAutoEventModels(patch.AutoEvents)
	// }
	// if patch.Protocols != nil {
	// 	device.Protocols = dtos.ToProtocolModels(patch.Protocols)
	// }
	if patch.Tags != nil {
		device.Tags = patch.Tags
	}
	if patch.Properties != nil {
		device.Properties = patch.Properties
	}
}

func NewAddDeviceRequest(dto models.Device) AddDeviceRequest {
	return AddDeviceRequest{
		BaseRequest: dtoCommon.NewBaseRequest(),
		Device:      dto,
	}
}

func NewUpdateDeviceRequest(dto UpdateDevice) UpdateDeviceRequest {
	return UpdateDeviceRequest{
		BaseRequest: dtoCommon.NewBaseRequest(),
		Device:      dto,
	}
}
